#include "StackOSI.h"
#include "LayerFactory.h"
#include <algorithm>
#include "StackUtils.h"

CStackOSI::CStackOSI(void)
{
}

CStackOSI::~CStackOSI(void)
{
	layers.clear();
}

const boost::shared_ptr<ILayer>& CStackOSI::getLayer( const std::string& name ) const
{
	citerator it = std::find_if(cbegin(), cend(), LayerComparer(name));
	
	if(it == cend()) 
	{
		throw new std::exception();
	}

	return *it;

}

void CStackOSI::addLayer( const boost::shared_ptr<ILayer>& layer )
{
	getContainerToModify().push_back(layer);
}

void CStackOSI::removeLayer( const std::string& name )
{
	getContainerToModify().erase(std::remove_if(begin(), end(), LayerComparer(name)));
}

StackUtils::e_Status CStackOSI::Encode( CPacket& packet )
{
	StackUtils::e_Status retCode = StackUtils::E_SUCCESS;

	for (iterator it = begin() ; it != end() ;++it)
	{
		boost::shared_ptr<ILayer> layer = *it;

		if (layer)
		{
			layer->Encode(packet);
		}
	}

	return retCode;
}

void CStackOSI::add( const boost::shared_ptr<ILayer>& element )
{
	getContainerToModify().push_back(element);
}

StackUtils::e_Status CStackOSI::Decode( CPacket& packet )
{
	StackUtils::e_Status retCode = StackUtils::E_SUCCESS;

	for (riterator it = rbegin() ; it != rend() ; ++it)
	{
		boost::shared_ptr<ILayer> layer = *it;

		if (layer)
		{
			layer->Decode(packet);
		}
	}

	return retCode;
}

CStackOSI& CStackOSI::getInstance()
{
	static CStackOSI instance;

	return instance;
}

void CStackOSI::init()
{
	boost::shared_ptr<ILayer> Layer1(CLayerFactory::CreateLayer("PHYSICAL"));
	boost::shared_ptr<ILayer> Layer2(CLayerFactory::CreateLayer("DATALINK"));
	boost::shared_ptr<ILayer> Layer3(CLayerFactory::CreateLayer("NETWORK"));
	boost::shared_ptr<ILayer> Layer4(CLayerFactory::CreateLayer("TRANSPORT"));
	boost::shared_ptr<ILayer> Layer5(CLayerFactory::CreateLayer("SESSION"));
	boost::shared_ptr<ILayer> Layer6(CLayerFactory::CreateLayer("PRESENTATION"));
	boost::shared_ptr<ILayer> Layer7(CLayerFactory::CreateLayer("APPLICATION"));

	add(Layer1);
	add(Layer2);
	add(Layer3);
	add(Layer4);
	add(Layer5);
	add(Layer6);
	add(Layer7);

	std::for_each(begin(), end(),  StackUtils::init_layer);

}
